<html>
    <head>
        <title>通用存储库</title>
    </head>
    <body>
        <script>
             /*
                        一、通用存储库  

                                  ABP可以为每个聚合根或实体提供一个默认通用存储库。你可以将IRepository<TEntity, TKey>注入你的服务并执行标准的CRUD操作。

                                  应正确配置数据库提供程序层，以便能够使用默认的通用存储库。如果你已经使用启动模板创建了项目，则已经完成。

                                  如果没有，请参阅数据库提供程序文档（EF Core/MongoDB）对其进行配置。

                                  1、默认存储库用法：

                                                eg： 通用存储的用法

                                                                using System;
                                                                using System.Threading.Tasks;
                                                                using Volo.Abp.Application.Services;
                                                                using Volo.Abp.Domain.Repositories;

                                                                namespace Demo
                                                                {
                                                                    public class PersonAppService : ApplicationService
                                                                    {
                                                                        private readonly IRepository<Person, Guid> _personRepository;  // 注入通用存储

                                                                        public PersonAppService(IRepository<Person, Guid> personRepository)
                                                                        {
                                                                            _personRepository = personRepository;
                                                                        }

                                                                        public async Task CreateAsync(CreatePersonDto input)
                                                                        {
                                                                            var person = new Person(input.Name);

                                                                            await _personRepository.InsertAsync(person);
                                                                        }

                                                                        public async Task<int> GetCountAsync(string filter)
                                                                        {
                                                                            return await _personRepository.CountAsync(p => p.Name.Contains(filter));  // 使用通用存储
                                                                        }
                                                                    }
                                                                }

                                                        PersonAppService简单的在它的构造函数中注入IRepository<Person, Guid>。     
                                                        
                                                        CreateAsync方法使用InsertAsync来保存新实体。

                                                        GetCountAsync方法获取数据库中所有人员的过滤计数。


                                 2、默认存储库中的方法：

                                            通用存储库提供了一些开箱即用的标准CRUD功能：

                                                    ①、GetAsync：通过其Id或谓词（lambda表达式）返回单个实体。

                                                                    如果未找到请求的实体，则抛出EntityNotFoundException。

                                                                    如果存在多个具有给定谓词的实体，则抛出InvalidOperationException。

                                                    ②、FindAsync：通过其Id或谓词（lambda表达式）返回单个实体。

                                                                    如果未找到请求的实体，则返回null。

                                                                    如果存在多个具有给定谓词的实体，则抛出InvalidOperationException。

                                                    ③、InsertAsync：将新实体插入数据库。

                                                    ④、UpdateAsync：更新数据库中的现有实体。

                                                    ⑤、DeleteAsync：从数据库中删除给定的实体。

                                                                    此方法有一个重载，它采用谓词（lambda表达式）来删除多个实体以满足给定条件。

                                                                    如果要删除单个实体，请使用GetAsync或FindAsync获取它。
                                                        
                                                    ⑥、GetListAsync：返回数据库中所有实体的列表。

                                                    ⑦、GetPagedListAsync：返回有限的实体列表。获取skipCount、maxResultCount和sorting参数。

                                                    ⑧、GetCountAsync：获取数据库中所有实体的计数。

                                            这些方法有重载。

                                                    提供UpdateAsync和DeleteAsync方法以按实体对象或其ID更新或删除实体。

                                                    提供DeleteAsync方法以通过过滤器删除多个实体。



                                 3、通过存储库进行Linq查询

                                            存储库提供GetQueryableAsync()方法，该方法返回一个IQueryable<TEntity>对象。    

                                            你可以使用此对象对数据库中的实体执行LINQ查询。

                                                    ①、标准Linq
                                                            eg:  在存储库基础上使用Linq

                                                                    using System;
                                                                    using System.Linq;
                                                                    using System.Collections.Generic;
                                                                    using System.Threading.Tasks;
                                                                    using Volo.Abp.Application.Services;
                                                                    using Volo.Abp.Domain.Repositories;

                                                                    namespace Demo
                                                                    {
                                                                        public class PersonAppService : ApplicationService
                                                                        {
                                                                            private readonly IRepository<Person, Guid> _personRepository;

                                                                            public PersonAppService(IRepository<Person, Guid> personRepository)
                                                                            {
                                                                                _personRepository = personRepository;
                                                                            }

                                                                            public async Task<List<PersonDto>> GetListAsync(string filter)
                                                                            {
                                                                                // 获取 IQueryable<Person> 对象
                                                                                IQueryable<Person> queryable = await _personRepository.GetQueryableAsync();

                                                                                // 创建Linq查询
                                                                                var query = from person in queryable
                                                                                    where person.Name == filter
                                                                                    orderby person.Name
                                                                                    select person;

                                                                                // 执行Linq查询
                                                                                var people = query.ToList();

                                                                                // 映射为Dto
                                                                                return people.Select(p => new PersonDto {Name = p.Name}).ToList();
                                                                            }
                                                                        }
                                                                    }

                                                    ②、方法Linq

                                                            你还可以使用LINQ扩展方法：

                                                                    public async Task<List<PersonDto>> GetListAsync(string filter)
                                                                    {
                                                                        // 获取IQueryable<Person>对象
                                                                        IQueryable<Person> queryable = await _personRepository.GetQueryableAsync();

                                                                        // 执行方法Linq查询
                                                                        var people = queryable
                                                                            .Where(p => p.Name.Contains(filter))
                                                                            .OrderBy(p => p.Name)
                                                                            .ToList();

                                                                        // 映射为Dto返回
                                                                        return people.Select(p => new PersonDto {Name = p.Name}).ToList();
                                                                    }


                                 4、批量操作  
                                 
                                            有一些方法可以在数据库中执行批量操作：

                                                    ①、InsertManyAsync

                                                    ②、UpdateManyAsync

                                                    ③、DeleteManyAsync

                                            这些方法适用于多个实体，如果底层数据库提供程序支持，则可以利用批量操作。


                                                当你使用UpdateManyAsync和DeleteManyAsync方法时，可能无法进行乐观并发控制。



                                 5、软删除、硬删除

                                            DeleteAsync方法不会删除该实体。软删除实体在数据库中被标记为“已删除”。
                                            
                                            数据过滤器系统确保软删除的实体不会正常从数据库中检索。

                                            如果你的实体是软删除实体，你可以使用HardDeleteAsync方法从数据库中物理删除该实体以防需要。

                                            有关软删除的更多信息，请参阅"数据过滤文档"。



                                6、直接删除  Delete Direct

                                            DeleteDirectAsync方法删除所有适合给定谓词的实体。它直接从数据库中删除实体，而不获取它们。


                                            某些功能（如软删除、多租户和审计日志记录）可能会因为直接删除二不起作用，因此在需要时请谨慎使用此方法。

                                            如果你需要这些功能，请使用DeleteAsync方法。

                                            目前只有EF Core支持它，对于不支持直接删除的ORM，我们将回退到现有的DeleteAsync方法。



                                7、确保实体存在

                                            EnsureExistsAsync扩展方法接受实体id或实体查询表达式以确保实体存在，否则，它将抛出EntityNotFoundException。



                                
                                8、启用/禁用更改跟踪   Enabling / Disabling the Change Tracking

                                            ABP提供存储库扩展方法和属性，可用于控制底层数据库提供程序中查询实体的更改跟踪行为。

                                            如果你出于只读目的从数据库中查询许多实体，禁用更改跟踪可以提高性能。

                                            查询单个或几个实体不会产生太大的性能差异，但你可以随时自由使用它。


                                            注意：

                                                如果底层数据库提供程序不支持更改跟踪，那么这个系统将没有任何效果。
                                                
                                                Entity Framework Core支持更改跟踪，例如，而MongoDB提供程序不支持它。


                                            ①、用于更改跟踪的存储库扩展方法

                                                     除非你明确禁用它，否则将启用更改跟踪。

                                                        (1)、使用 DisableTracking 扩展方法
                                                                eg: 使用 DisableTracking 扩展方法

                                                                        public class MyDemoService : ApplicationService
                                                                        {
                                                                            private readonly IRepository<Person, Guid> _personRepository;

                                                                            public MyDemoService(IRepository<Person, Guid> personRepository)
                                                                            {
                                                                                _personRepository = personRepository;
                                                                            }

                                                                            public async Task DoItAsync()
                                                                            {
                                                                                // 在这里，实体更改追踪功能还是开启的。


                                                                                
                                                                                using (_personRepository.DisableTracking()) // 此时禁用实体更改追踪
                                                                                {
                                                                                    // 不再追踪实体更改
                                                                                    var list = await _personRepository.GetPagedListAsync(0, 100, "Name ASC");
                                                                                }
                                                                                
                                                                                // 此处，实体更改追踪又被重新启用。
                                                                            }
                                                                        }

                                                                    DisableTracking扩展方法返回一个IDisposable对象，因此一旦using块结束，你可以安全地将更改跟踪行为恢复到以前的状态。

                                                                    基本上，DisableTracking方法确保在using块内禁用更改跟踪，但不会影响using块之外。

                                                                    这意味着，如果更改跟踪已经禁用，DisableTracking和一次性返回值什么也不做。

                                                                    EnableTracking()方法的工作方式与DisableTracking()方法完全相反。
                                                                    
                                                                    你通常不会使用它（因为默认情况下已经启用了更改跟踪），但如果你需要，它就在那里。


                                                        
                                                        (2)、使用 "控制变更跟踪的特性"  [DisableEntityChangeTracking] 和 [EnableEntityChangeTracking ]

                                                                    eg:  在方法上使用 [DisableEntityChangeTracking]特性


                                                                                [DisableEntityChangeTracking]
                                                                                public virtual async Task<List<PersonDto>> GetListAsync()
                                                                                {
                                                                                    // 此处禁用实体更改追踪

                                                                                    var people = await _personRepository.GetListAsync();
                                                                                    return ObjectMapper.Map<List<Person>, List<PersonDto>(people);
                                                                                }

                                                                      EnableEntityChangeTracking可用于相反的目的，它确保为给定方法启用更改跟踪。

                                                                      由于更改跟踪默认启用，因此只有当你知道你的方法是从禁用更改跟踪的上下文中调用时，才可能需要EnableEntityChangeTracking。

                                                                      [DisableEntityChangeTracking] 和 [EnableEntityChangeTracking] 属性可以用于方法或类（这会影响所有类方法）。
                                                     
                                                     
                                                                      ABP使用动态代理来使这些属性工作。这里有一些规则：

                                                                                如果你没有通过接口注入服务（如IPersonAppService），则服务的方法必须是virtual。
                                                                                
                                                                                否则，动态代理/拦截系统无法工作。

                                                                                仅拦截async方法（返回Task或Task<T>的方法）。


                                            ★、更改跟踪行为不会影响从InsertAsync和UpdateAsync方法返回的跟踪实体对象。
                                            
                                                 从这些方法返回的对象始终被跟踪（如果底层提供程序具有更改跟踪功能），你对这些对象所做的任何更改都将保存到数据库中。

                    */
        </script>
    </body>
</html>